In [1]:
# Libraries
import dash
from dash import dcc
from dash import html
from dash.dependencies import Input, Output
from jupyter_dash import JupyterDash
import pandas as pd
import dash_bootstrap_components as dbc
from sklearn.metrics import roc_auc_score, accuracy_score, precision_score, recall_score, f1_score, auc
from sklearn.metrics import confusion_matrix, roc_curve, roc_auc_score, accuracy_score, classification_report
from scikitplot.metrics import plot_lift_curve
import plotly.graph_objects as go
import plotly.tools as tls
import plotly.express as px
import plotly.figure_factory as ff
import matplotlib.pyplot as plt
from io import BytesIO
import scikitplot as skplt
from dash import dash_table
import numpy as np
import io
import base64
import matplotlib.pyplot as plt
from scikitplot.metrics import plot_lift_curve
import pickle
import shap


# Start jupyterdash application
app = JupyterDash(__name__,  external_stylesheets=[dbc.themes.BOOTSTRAP])
C:\Users\albar\anaconda3\envs\tfm_practico\lib\site-packages\shap\utils\_clustering.py:35: NumbaDeprecationWarning:

The 'nopython' keyword argument was not supplied to the 'numba.jit' decorator. The implicit default value for this argument is currently False, but it will be changed to True in Numba 0.59.0. See https://numba.readthedocs.io/en/stable/reference/deprecation.html#deprecation-of-object-mode-fall-back-behaviour-when-using-jit for details.

C:\Users\albar\anaconda3\envs\tfm_practico\lib\site-packages\shap\utils\_clustering.py:54: NumbaDeprecationWarning:

The 'nopython' keyword argument was not supplied to the 'numba.jit' decorator. The implicit default value for this argument is currently False, but it will be changed to True in Numba 0.59.0. See https://numba.readthedocs.io/en/stable/reference/deprecation.html#deprecation-of-object-mode-fall-back-behaviour-when-using-jit for details.

C:\Users\albar\anaconda3\envs\tfm_practico\lib\site-packages\shap\utils\_clustering.py:63: NumbaDeprecationWarning:

The 'nopython' keyword argument was not supplied to the 'numba.jit' decorator. The implicit default value for this argument is currently False, but it will be changed to True in Numba 0.59.0. See https://numba.readthedocs.io/en/stable/reference/deprecation.html#deprecation-of-object-mode-fall-back-behaviour-when-using-jit for details.

C:\Users\albar\anaconda3\envs\tfm_practico\lib\site-packages\shap\utils\_clustering.py:69: NumbaDeprecationWarning:

The 'nopython' keyword argument was not supplied to the 'numba.jit' decorator. The implicit default value for this argument is currently False, but it will be changed to True in Numba 0.59.0. See https://numba.readthedocs.io/en/stable/reference/deprecation.html#deprecation-of-object-mode-fall-back-behaviour-when-using-jit for details.

C:\Users\albar\anaconda3\envs\tfm_practico\lib\site-packages\shap\utils\_clustering.py:77: NumbaDeprecationWarning:

The 'nopython' keyword argument was not supplied to the 'numba.jit' decorator. The implicit default value for this argument is currently False, but it will be changed to True in Numba 0.59.0. See https://numba.readthedocs.io/en/stable/reference/deprecation.html#deprecation-of-object-mode-fall-back-behaviour-when-using-jit for details.

C:\Users\albar\anaconda3\envs\tfm_practico\lib\site-packages\shap\links.py:5: NumbaDeprecationWarning:

The 'nopython' keyword argument was not supplied to the 'numba.jit' decorator. The implicit default value for this argument is currently False, but it will be changed to True in Numba 0.59.0. See https://numba.readthedocs.io/en/stable/reference/deprecation.html#deprecation-of-object-mode-fall-back-behaviour-when-using-jit for details.

C:\Users\albar\anaconda3\envs\tfm_practico\lib\site-packages\shap\links.py:10: NumbaDeprecationWarning:

The 'nopython' keyword argument was not supplied to the 'numba.jit' decorator. The implicit default value for this argument is currently False, but it will be changed to True in Numba 0.59.0. See https://numba.readthedocs.io/en/stable/reference/deprecation.html#deprecation-of-object-mode-fall-back-behaviour-when-using-jit for details.

C:\Users\albar\anaconda3\envs\tfm_practico\lib\site-packages\shap\links.py:15: NumbaDeprecationWarning:

The 'nopython' keyword argument was not supplied to the 'numba.jit' decorator. The implicit default value for this argument is currently False, but it will be changed to True in Numba 0.59.0. See https://numba.readthedocs.io/en/stable/reference/deprecation.html#deprecation-of-object-mode-fall-back-behaviour-when-using-jit for details.

C:\Users\albar\anaconda3\envs\tfm_practico\lib\site-packages\shap\links.py:20: NumbaDeprecationWarning:

The 'nopython' keyword argument was not supplied to the 'numba.jit' decorator. The implicit default value for this argument is currently False, but it will be changed to True in Numba 0.59.0. See https://numba.readthedocs.io/en/stable/reference/deprecation.html#deprecation-of-object-mode-fall-back-behaviour-when-using-jit for details.

C:\Users\albar\anaconda3\envs\tfm_practico\lib\site-packages\shap\utils\_masked_model.py:363: NumbaDeprecationWarning:

The 'nopython' keyword argument was not supplied to the 'numba.jit' decorator. The implicit default value for this argument is currently False, but it will be changed to True in Numba 0.59.0. See https://numba.readthedocs.io/en/stable/reference/deprecation.html#deprecation-of-object-mode-fall-back-behaviour-when-using-jit for details.

C:\Users\albar\anaconda3\envs\tfm_practico\lib\site-packages\shap\utils\_masked_model.py:385: NumbaDeprecationWarning:

The 'nopython' keyword argument was not supplied to the 'numba.jit' decorator. The implicit default value for this argument is currently False, but it will be changed to True in Numba 0.59.0. See https://numba.readthedocs.io/en/stable/reference/deprecation.html#deprecation-of-object-mode-fall-back-behaviour-when-using-jit for details.

C:\Users\albar\anaconda3\envs\tfm_practico\lib\site-packages\shap\utils\_masked_model.py:428: NumbaDeprecationWarning:

The 'nopython' keyword argument was not supplied to the 'numba.jit' decorator. The implicit default value for this argument is currently False, but it will be changed to True in Numba 0.59.0. See https://numba.readthedocs.io/en/stable/reference/deprecation.html#deprecation-of-object-mode-fall-back-behaviour-when-using-jit for details.

C:\Users\albar\anaconda3\envs\tfm_practico\lib\site-packages\shap\utils\_masked_model.py:439: NumbaDeprecationWarning:

The 'nopython' keyword argument was not supplied to the 'numba.jit' decorator. The implicit default value for this argument is currently False, but it will be changed to True in Numba 0.59.0. See https://numba.readthedocs.io/en/stable/reference/deprecation.html#deprecation-of-object-mode-fall-back-behaviour-when-using-jit for details.

C:\Users\albar\anaconda3\envs\tfm_practico\lib\site-packages\shap\maskers\_tabular.py:186: NumbaDeprecationWarning:

The 'nopython' keyword argument was not supplied to the 'numba.jit' decorator. The implicit default value for this argument is currently False, but it will be changed to True in Numba 0.59.0. See https://numba.readthedocs.io/en/stable/reference/deprecation.html#deprecation-of-object-mode-fall-back-behaviour-when-using-jit for details.

C:\Users\albar\anaconda3\envs\tfm_practico\lib\site-packages\shap\maskers\_tabular.py:197: NumbaDeprecationWarning:

The 'nopython' keyword argument was not supplied to the 'numba.jit' decorator. The implicit default value for this argument is currently False, but it will be changed to True in Numba 0.59.0. See https://numba.readthedocs.io/en/stable/reference/deprecation.html#deprecation-of-object-mode-fall-back-behaviour-when-using-jit for details.

C:\Users\albar\anaconda3\envs\tfm_practico\lib\site-packages\shap\maskers\_image.py:175: NumbaDeprecationWarning:

The 'nopython' keyword argument was not supplied to the 'numba.jit' decorator. The implicit default value for this argument is currently False, but it will be changed to True in Numba 0.59.0. See https://numba.readthedocs.io/en/stable/reference/deprecation.html#deprecation-of-object-mode-fall-back-behaviour-when-using-jit for details.

C:\Users\albar\anaconda3\envs\tfm_practico\lib\site-packages\shap\explainers\_partition.py:676: NumbaDeprecationWarning:

The 'nopython' keyword argument was not supplied to the 'numba.jit' decorator. The implicit default value for this argument is currently False, but it will be changed to True in Numba 0.59.0. See https://numba.readthedocs.io/en/stable/reference/deprecation.html#deprecation-of-object-mode-fall-back-behaviour-when-using-jit for details.

The 'nopython' keyword argument was not supplied to the 'numba.jit' decorator. The implicit default value for this argument is currently False, but it will be changed to True in Numba 0.59.0. See https://numba.readthedocs.io/en/stable/reference/deprecation.html#deprecation-of-object-mode-fall-back-behaviour-when-using-jit for details.
The 'nopython' keyword argument was not supplied to the 'numba.jit' decorator. The implicit default value for this argument is currently False, but it will be changed to True in Numba 0.59.0. See https://numba.readthedocs.io/en/stable/reference/deprecation.html#deprecation-of-object-mode-fall-back-behaviour-when-using-jit for details.
In [2]:
# Load data
X_train_rfe = pd.read_parquet("../data/processed/X_train_rfe.parquet")
y_train = pd.read_parquet("../data/processed/y_train.parquet")['isfraud']
y_train = y_train.reset_index(drop=True)

X_test_rfe = pd.read_parquet("../data/processed/X_test_rfe.parquet")
y_test = pd.read_parquet("../data/processed/y_test.parquet")['isfraud']
y_test = y_test.reset_index(drop=True)

fraud_df = pd.read_parquet('../data/processed/fraud_df_initial.parquet')

# Load final model
with open('../models/xgboost_optimizado.pickle', 'rb') as f:
    CV_xgb = pickle.load(f)
In [3]:
# Function to get the metrics
def evaluate_model(ytest, ypred, ypred_proba = None):
    metrics_dict = {}
    if ypred_proba is not None:
        metrics_dict['roc_auc'] = roc_auc_score(ytest, ypred_proba[:, 1])
    metrics_dict['accuracy'] = accuracy_score(ytest, ypred)
    metrics_dict['precision'] = precision_score(ytest, ypred)
    metrics_dict['recall'] = recall_score(ytest, ypred)
    metrics_dict['f1'] = f1_score(ytest, ypred)
    return metrics_dict
In [4]:
# EDA

# Graph of target distribution 
fraud_count = fraud_df[fraud_df['isfraud'] == 1].shape[0]
non_fraud_count = fraud_df[fraud_df['isfraud'] == 0].shape[0]
fig = go.Figure(data=[go.Pie(labels=['Fraud', 'Non-Fraud'], 
                             values=[fraud_count, non_fraud_count],
                             hole=.3)])

# Graphs of categorical variables
fraud_card4 = fraud_df[['card4', 'isfraud']].groupby('card4')['isfraud'].sum().reset_index()
fraud_card6 = fraud_df[['card6', 'isfraud']].groupby('card6')['isfraud'].sum().reset_index()
fig_card4 = go.Figure(data=[go.Bar(
    x=fraud_card4['card4'], 
    y=fraud_card4['isfraud']
)])

fig_card6 = go.Figure(data=[go.Bar(
    x=fraud_card6['card6'], 
    y=fraud_card6['isfraud']
)])

# Graphs of transaction evolution
fraud_transactions = fraud_df.groupby(['isfraud', 'transaction_day']).size().reset_index()
fraud_transactions.columns = ['isfraud', 'transaction_day', 'count']
fig_transactions = px.line(fraud_transactions, 
                           x='transaction_day', 
                           y='count', 
                           color='isfraud', 
                           labels={'isfraud':'Fraud'},
                           title='Fraudulent and Non-Fraudulent Transactions Over Time')

fraudulent_transactions = fraud_df[fraud_df['isfraud'] == 1].groupby('transaction_day').size().reset_index()
fraudulent_transactions.columns = ['transaction_day', 'count']
fig_fraudulent_transactions = px.line(fraudulent_transactions, 
                                      x='transaction_day', 
                                      y='count',
                                      title='Fraudulent Transactions Over Time')



fig_fraudulent_transactions.update_layout(yaxis_title='Transaction Count')

# Graphs of transactions per Hour
fraud_hourly = fraud_df.groupby(['isfraud', 'transaction_hour']).size().reset_index()
fraud_hourly.columns = ['isfraud', 'transaction_hour', 'count']
fig_hourly_fraud = px.line(fraud_hourly, 
                           x='transaction_hour', 
                           y='count', 
                           color='isfraud',
                           labels={'isfraud':'Fraud'},
                           title='Fraudulent and Non-Fraudulent Transactions by Hour')

fraud_hourly_specific = fraud_df[fraud_df['isfraud'] == 1].groupby('transaction_hour').size().reset_index()
fraud_hourly_specific.columns = ['transaction_hour', 'count']
fig_hourly_fraud_specific = px.line(fraud_hourly_specific, 
                                    x='transaction_hour', 
                                    y='count', 
                                    title='Fraudulent Transactions by Hour of the Day')

# Histogram of the quantities
fraud_amt = fraud_df[fraud_df['isfraud'] == 1]['transactionamt']
fig_amt = px.histogram(fraud_amt, nbins=50, labels={'value': 'Amount'})

fig_card4.update_layout(title_text='Fraud by Card Provider', yaxis_title='Value')
fig_card6.update_layout(title_text='Fraud by Card Type', yaxis_title='Value')
fig_hourly_fraud_specific.update_layout(yaxis_title='Transaction Count', xaxis_title='Hour of the Day')
fig_hourly_fraud.update_layout(yaxis_title='Transaction Count', xaxis_title='Hour of the Day')
fig_amt.update_xaxes(title_text='Amount')
fig_amt.update_yaxes(title_text='Frequency')
In [5]:
# Model results

# Make model predictions
pred = CV_xgb.predict(X_test_rfe)
pred_proba = CV_xgb.predict_proba(X_test_rfe)

# Metrics table
metrics = evaluate_model(y_test, pred, pred_proba)
data = [
    {"Metric": "Accuracy", "Value": f"{metrics['accuracy']:.4f}"},
    {"Metric": "Precision", "Value": f"{metrics['precision']:.4f}"},
    {"Metric": "Recall", "Value": f"{metrics['recall']:.4f}"},
    {"Metric": "F1 score", "Value": f"{metrics['f1']:.4f}"},
    {"Metric": "AUC ROC", "Value": f"{metrics['roc_auc']:.4f}"},
]

# Confusion matrix
cm = confusion_matrix(y_test, pred)
z_text = [[str(y) for y in x] for x in cm]
colorscale = [
    [0.0, '#FFFFFF'],
    [0.1, '#a1a6ed'],
    [0.2, '#8a97f2'],
    [0.4, '#8b93f7'],
    [0.6, '#7e87f7'],
    [0.8, '#737dfa'],
    [1.0, '#646FFA']
]
fig_confusion_matrix = ff.create_annotated_heatmap(z=cm, annotation_text=z_text, colorscale=colorscale, showscale=True)
fig_confusion_matrix.update_layout(
    title='Confusion Matrix',
    xaxis=dict(title='Predicted', tickvals=[0,1], ticktext=['0', '1']),
    yaxis=dict(title='Observed', tickvals=[0,1], ticktext=['1', '0'], autorange="reversed"),
)

# ROC CURVE
yhat = pred_proba[:, 1]
fpr, tpr, thresholds = roc_curve(y_test, yhat)
fig_roc_curve = go.Figure()
fig_roc_curve.add_trace(go.Scatter(x=[0, 1], y=[0, 1], mode='lines', name='No Skill', line=dict(dash='dash')))
fig_roc_curve.add_trace(go.Scatter(x=fpr, y=tpr, mode='lines', name='Gradient Boosting'))
fig_roc_curve.update_layout(title="ROC Curve",xaxis_title="False Positive Rate",yaxis_title="True Positive Rate",
                            autosize=False,width=600,height=470,margin=dict(l=0, r=0, b=100, t=100, pad=4),)

# Lift curve
from cycler import cycler
colors = ['#646FFA','#EF553B', '#61FAC1']
plt.figure(figsize=(6, 4.7))
plt.rcParams['axes.prop_cycle'] = cycler(color=colors)
skplt.metrics.plot_lift_curve(y_test, pred_proba)
plt.grid(color='#FFFFFF', linestyle='-', linewidth=0.5) # CuadrĆ­cula
plt.gca().set_facecolor('#E5ECF6') # Color de fondo
plt.gca().spines['top'].set_visible(False)
plt.gca().spines['right'].set_visible(False)
plt.gca().spines['bottom'].set_visible(False)
plt.gca().spines['left'].set_visible(False)
lines = plt.gca().lines
for line in lines:
    line.set_linewidth(1.5) 
plt.subplots_adjust(right=0.8)
plt.legend(facecolor='white', frameon=False, bbox_to_anchor=(1, 1), loc='upper left')
buffer = BytesIO()
plt.savefig(buffer, format='png')
buffer.seek(0)
encoded_image = base64.b64encode(buffer.getvalue()).decode('utf-8')
plt.show()

#Curva ganancia
plt.figure(figsize=(6, 4.7))
skplt.metrics.plot_cumulative_gain(y_test, pred_proba)
plt.grid(color='#FFFFFF', linestyle='-', linewidth=0.5) # CuadrĆ­cula
plt.gca().set_facecolor('#E5ECF6') # Color de fondo
plt.gca().spines['top'].set_visible(False)
plt.gca().spines['right'].set_visible(False)
plt.gca().spines['bottom'].set_visible(False)
plt.gca().spines['left'].set_visible(False)
lines = plt.gca().lines
for line in lines:
    line.set_linewidth(1.5)
plt.subplots_adjust(right=0.8)
plt.legend(facecolor='white', frameon=False, bbox_to_anchor=(1, 1), loc='upper left')
buffer = BytesIO()
plt.savefig(buffer, format='png')
plt.close()
buffer.seek(0)
encoded_gain_image = base64.b64encode(buffer.getvalue()).decode('utf-8')
<Figure size 600x470 with 0 Axes>
<Figure size 600x470 with 0 Axes>
In [6]:
best_params_df = pd.DataFrame([CV_xgb.best_params_])
best_params_df = best_params_df.transpose()
best_params_df.reset_index(inplace=True)
best_params_df.columns = ['Parameter', 'Value']
new_row = pd.DataFrame({"Parameter": ["Resampler"], "Value": ["RandomUnderSampler"]})
best_params_df = pd.concat([best_params_df, new_row], ignore_index=True)
dash_table.DataTable(
    data=best_params_df.to_dict('records'),
    columns=[{'name': i, 'id': i} for i in best_params_df.columns],
    style_cell={'textAlign': 'center'},
    style_header={
        'backgroundColor': 'white',
        'fontWeight': 'bold'
    }
)
Out[6]:
DataTable(data=[{'Parameter': 'classifier__colsample_bytree', 'Value': 0.6}, {'Parameter': 'classifier__learning_rate', 'Value': 0.1}, {'Parameter': 'classifier__max_depth', 'Value': 10.0}, {'Parameter': 'classifier__min_child_weight', 'Value': 5.0}, {'Parameter': 'classifier__n_estimators', 'Value': 1200.0}, {'Parameter': 'classifier__subsample', 'Value': 0.8}, {'Parameter': 'Resampler', 'Value': 'RandomUnderSampler'}], columns=[{'name': 'Parameter', 'id': 'Parameter'}, {'name': 'Value', 'id': 'Value'}], style_cell={'textAlign': 'center'}, style_header={'backgroundColor': 'white', 'fontWeight': 'bold'})
In [7]:
model = CV_xgb.best_estimator_.named_steps['classifier']
explainer = shap.TreeExplainer(model)
shap_values = explainer.shap_values(X_test_rfe)
ntree_limit is deprecated, use `iteration_range` or model slicing instead.
In [8]:
shap.summary_plot(shap_values, X_test_rfe, show=False)
plt.savefig("summary_plot.png")
plt.clf()
No data for colormapping provided via 'c'. Parameters 'vmin', 'vmax' will be ignored
<Figure size 800x950 with 0 Axes>
In [9]:
fraud_index = y_test[y_test == 1].index[0]
not_fraud_index = y_test[y_test == 0].index[0]

fraud_index_X = X_test_rfe.index.get_loc(fraud_index)
not_fraud_index_X = X_test_rfe.index.get_loc(not_fraud_index)

shap.force_plot(explainer.expected_value, shap_values[fraud_index_X, :], X_test_rfe.loc[fraud_index, :], show=False, matplotlib=True)
plt.savefig("force_plot_fraud.png")
plt.clf() 

shap.force_plot(explainer.expected_value, shap_values[not_fraud_index_X, :], X_test_rfe.loc[not_fraud_index, :], show=False, matplotlib=True)
plt.savefig("force_plot_not_fraud.png")
plt.clf()
In [10]:
shap.dependence_plot("transactionamt", shap_values, X_test_rfe, interaction_index="transaction_hour", show=False)
plt.savefig("dependence_plot.png")
plt.clf()
In [11]:
shap.summary_plot(shap_values, X_test_rfe, plot_type="bar", show=False)
plt.savefig("summary_plot_bar.png")
plt.clf() # Limpia la figura
In [12]:
encoded_image_summary = base64.b64encode(open('summary_plot.png', 'rb').read()).decode('ascii')
encoded_image_force_fraud = base64.b64encode(open('force_plot_fraud.png', 'rb').read()).decode('ascii')
encoded_image_force_not_fraud = base64.b64encode(open('force_plot_not_fraud.png', 'rb').read()).decode('ascii')
encoded_image_dependence = base64.b64encode(open('dependence_plot.png', 'rb').read()).decode('ascii')
encoded_image_bar = base64.b64encode(open('summary_plot_bar.png', 'rb').read()).decode('ascii')
In [13]:
fig_transactions.update_layout(height=400, width=500)
fig_fraudulent_transactions.update_layout(height=400, width=500)
fig_hourly_fraud.update_layout(height=400, width=500)
fig_hourly_fraud_specific.update_layout(height=400, width=500)

card_style = {
    'border': '1px solid #dee2e6',
    'borderRadius': '0.25rem',
    'boxShadow': '0 0.5rem 1rem rgba(0, 0, 0, 0.05)',
    'margin': '5px'
}

# Define el layout de la aplicación
app.layout = dbc.Container([
    dcc.Tabs([
        dbc.Tab(label='EDA', children=[
            html.H1('Exploratory Data Analysis', style={'textAlign': 'center', 'color': '#000608','margin': '20px'}),
            dbc.Row([
                dbc.Col(
                    dbc.Card([
                        dbc.CardHeader("Target Distribution"),
                        dbc.CardBody([
                            html.P("This graph reflects the main issue of these data, the large class imbalance."),
                            dcc.Graph(figure=fig, style={'height': '400px', 'width': '100%'}), 
                        ])
                    ], style={**card_style, 'height': '530px'}), width=4
                    ),
                dbc.Col(
                    dbc.Card([
                        dbc.CardHeader("Histogram of Fraudulent Transactions Amount"),
                        dbc.CardBody([
                            dcc.Graph(figure=fig_amt, style={'height': '100%', 'width': '100%'}),
                        ])
                    ], style={**card_style, 'height': '530px'}), width=8
                )
            ], style={'display': 'flex', 'justify-content': 'space-between'}),

            dbc.Card([
                dbc.CardHeader("Types of Cards Most Affected"),
                dbc.CardBody([
                    dbc.Row([
                        dbc.Col(dcc.Graph(figure=fig_card4), width=6),
                        dbc.Col(dcc.Graph(figure=fig_card6), width=6),
                    ])
                ])
            ], style=card_style),

            dbc.Row([
                dbc.Col(
                    dbc.Card([
                        dbc.CardHeader("Transactions Evolution"),
                        dbc.CardBody([
                            dcc.Graph(figure=fig_transactions),
                            dcc.Graph(figure=fig_fraudulent_transactions)
                        ])
                    ], style={**card_style, 'overflow': 'hidden'}), width=6
                ),
                dbc.Col(
                    dbc.Card([
                        dbc.CardHeader("Evolution of Transactions per Hour"),
                        dbc.CardBody([
                            dcc.Graph(figure=fig_hourly_fraud),
                            dcc.Graph(figure=fig_hourly_fraud_specific)
                        ])
                    ], style={**card_style, 'overflow': 'hidden'}), width=6
                )
            ], style={'margin-bottom': '10px'}),
        ]),
        dbc.Tab(label='Model Results', children=[
            html.H1("Final Model Results", style={'textAlign': 'center', 'color': '#000608','margin': '20px'}),
            dbc.Row([
                dbc.Col([
                    html.H3("XGBoost", style={'textAlign': 'center', 'fontSize':'81px'}),
                    dbc.Card([
                        dbc.CardHeader("Metrics"),
                        dbc.CardBody([
                            dash_table.DataTable(
                                id='table',
                                columns=[{"name": i, "id": i} for i in ["Metric", "Value"]],
                                data=data,
                                style_cell={'textAlign': 'center'},
                                style_header={
                                    'backgroundColor': '#grey', 
                                    'color': 'black',  
                                    'fontWeight': 'bold'
                                }
                            ),
                        ])
                    ], style={**card_style, 'height': '250px'})

                ], width=6),
                dbc.Col(
                    dbc.Card([
                        dbc.CardHeader("Best Model Parameters"),
                        dbc.CardBody([
                            dash_table.DataTable(
                                data=best_params_df.to_dict('records'),
                                columns=[{'name': i, 'id': i} for i in best_params_df.columns],
                                style_cell={'textAlign': 'center'},
                                style_header={
                                    'backgroundColor': '#grey', 
                                    'color': 'black',  
                                    'fontWeight': 'bold'
                                }
                            ),
                        ])
                    ], style={**card_style, 'height': '350px', 'justify-content': 'center',}), 
                )
            ], style={'display': 'flex', 'justify-content': 'space-between'}),
            dbc.Row([
                dbc.Col(
                    dbc.Card([
                        dbc.CardHeader("Confusion Matrix"),
                        dbc.CardBody([
                            dcc.Graph(figure=fig_confusion_matrix),
                        ])
                    ], style={**card_style, 'height': '555px'}), width=6
                ),
                dbc.Col(
                    dbc.Card([
                        dbc.CardHeader("ROC Curve"),
                        dbc.CardBody([
                            dcc.Graph(figure=fig_roc_curve),
                        ])
                    ], style={**card_style, 'height': '530px'}), width=6
                )
            ], style={'display': 'flex', 'justify-content': 'space-between','align-items': 'center'}),
            dbc.Row([
                dbc.Col(
                    dbc.Card([
                        dbc.CardHeader("Lift Curve"),
                        dbc.CardBody([
                            html.Img(src='data:image/png;base64,{}'.format(encoded_image), style={'width': '100%', 'height': '100%'})
                        ])
                    ], style={**card_style, 'height': '530px', 'justify-content': 'center',}), width=6
                ),
                dbc.Col(
                    dbc.Card([
                        dbc.CardHeader("Cumulative Gain Curve"),
                        dbc.CardBody([
                            html.Img(src='data:image/png;base64,{}'.format(encoded_gain_image), style={'width': '100%', 'height': '100%'})
                        ])
                    ], style={**card_style, 'height': '530px'}), width=6
                )
            ], style={'display': 'flex', 'justify-content': 'space-between','align-items': 'center'}),
        ]),
        dbc.Tab(label='Explanability', children=[
            html.H1("SHAP", style={'textAlign': 'center', 'color': '#000608','margin': '20px'}),

        dbc.Row([
            dbc.Col([
                dbc.Card([
                    dbc.CardHeader("Summary Plot"),
                    dbc.CardBody([
                        html.Img(src='data:image/png;base64,{}'.format(encoded_image_summary), style={'width': '100%', 'height': '100%'})
                    ])
                ], style=card_style)
            ], width=6),

            dbc.Col([
                dbc.Card([
                    dbc.CardHeader("Summary Bar Plot"),
                    dbc.CardBody([
                        html.Img(src='data:image/png;base64,{}'.format(encoded_image_bar), style={'width': '100%', 'height': '100%'})
                    ])
                ], style=card_style)
            ], width=6),
        ]),

        dbc.Row([
            dbc.Col([
                dbc.Card([
                    dbc.CardHeader("Dependence Plot"),
                    dbc.CardBody([
                        html.Img(src='data:image/png;base64,{}'.format(encoded_image_dependence), style={'width': '100%', 'height': '100%'})
                    ])
                ], style={**card_style, 'height': '390px','width': '530px',})
            ], width=5),
            dbc.Col([
                dbc.Row([
                    dbc.Col([
                        dbc.Card([
                            dbc.CardHeader("Force Plot (Fraud Transaction)"),
                            dbc.CardBody([
                                html.Img(src='data:image/png;base64,{}'.format(encoded_image_force_fraud), style={'width': '100%', 'height': '100%'})
                            ])
                        ], style={**card_style, 'height': '190px','width': '730px'})
                    ], width=10 ),

                    dbc.Col([
                        dbc.Card([
                            dbc.CardHeader("Force Plot (Non-fraud Transaction)"),
                            dbc.CardBody([
                                html.Img(src='data:image/png;base64,{}'.format(encoded_image_force_not_fraud), style={'width': '100%', 'height': '100%'})
                            ])
                        ], style={**card_style, 'height': '190px','width': '730px'})
                    ], width=10),
                ])
            ], width=6),
        ])
    ])
        ])
    ], style={'backgroundColor': '#f8f9fa', 'padding': '10px'})

# Ejecuta la aplicación
if __name__ == '__main__':
    app.run_server(debug=True)
Dash app running on http://127.0.0.1:8050/